package com.sky.orm;


import com.sky.annotation.Delete;
import com.sky.annotation.Insert;
import com.sky.annotation.Select;
import com.sky.annotation.Update;
import com.sky.dao.UserDao;
import com.sky.demo.MyBatisUtils;
import com.sky.domain.User;
import com.sky.unit.JdbcUnits;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.URL;
import java.util.*;

/**
 * @author chengjy
 * @date 2023/3/27
 */
public class Sqlsession {

    static {
        //类加载器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        //加载器资源下文件
        InputStream inputStream = contextClassLoader.getResourceAsStream("mysqlConfig.properties");
        System.out.println("自动注入Dao...");
        Properties prop = new Properties();
        //转字符流
        try {
            prop.load(new InputStreamReader(inputStream));
            String daoPath = prop.getProperty("component");
            URL resource = contextClassLoader.getResource(daoPath); } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private MyBatisUtils myBatisUtils = new MyBatisUtils();

    private JdbcUnits jdbcUnits = new JdbcUnits();

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        UserDao dao = new Sqlsession().getMapper(UserDao.class);
        User user = dao.getById(1L);
        System.out.println(user);
//
//        Map<String,Object> user = new HashMap<>();
//        user.put("id",1L);
//        user.put("sex","0");
//        user.put("phone","15173047216");
//        dao.update(user);
    }

    public void insert(String sql,Object obj){
        update(sql, obj);
    }
    public void delete(String sql,Object obj){
        update(sql, obj);
    }
    public void update(String sql,Object obj){
        SqlAndKey sqlAndKey = sqlHanding(sql);
        List<String> keyList = sqlAndKey.getKeyList();
        sql  = sqlAndKey.getNewSql().toString();
        try {
            Map<String, Object> map = myBatisUtils.paramHandle(keyList, obj);
            jdbcUnits.cudIkun(sql,keyList,map);
        } catch (Exception e) {
            System.out.println("出异常了");
            e.printStackTrace();
        }
    }

    /**
     * 操作单条数据
     * @param sql
     * @param obj
     * @param clazz
     * @param <T>
     * @return
     */
    public <T>T selectOne(String sql,Object obj,Class clazz){
        SqlAndKey sqlAndKey = sqlHanding(sql);
        List<String> keyList = sqlAndKey.getKeyList();
        sql  = sqlAndKey.getNewSql().toString();
        try {
            Map<String, Object> map = myBatisUtils.paramHandle(keyList, obj);
            Object o = jdbcUnits.selectIkun(sql, keyList, map, clazz);
            List<Object> objList = (List) o;
            return (T) objList.get(0);
        } catch (Exception e) {
            System.out.println("出异常了");
            e.printStackTrace();
        }
        return null;
    }

    public <T>List<T> selectList(String sql,Object obj,Class clazz){
        SqlAndKey sqlAndKey = sqlHanding(sql);
        List<String> keyList = sqlAndKey.getKeyList();
        sql  = sqlAndKey.getNewSql().toString();
        try {
            Map<String, Object> map = myBatisUtils.paramHandle(keyList, obj);
            return jdbcUnits.selectIkun(sql,keyList,map,clazz);
        } catch (Exception e) {
            System.out.println("出异常了");
            e.printStackTrace();
        }
        return null;
    }


    //方法目的可以在Sqlsession类中产生一个代理对象，代理原来的DAO作事
    //参数  代替的那个DAO UserDao.class
    //返回值  返回一个代理对象  理解为UserDao的代理（UserDao的子类）

    public <T>T getMapper(Class clazz){
        //1.类加载器
        ClassLoader classLoader = clazz.getClassLoader();
        //2、加载的类Class[]
        Class[]  interfaces = new Class[]{clazz};
        //3、具体该如何作事InvocationHandler 代理逻辑
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //invoke方法就是代理做事的方法service调用dao的时候，代理就帮助dao做事
                // 参数1-proxy(代理对象) 参数2-method(原dao的方法)参数3-args(原dao方法的参数)
                //--------------------------------------------------------------------------------------------------------------
                // 代理代替原来DAO做的事情---具体的事情
                // 调用当前Sglsession类中的执行方法-------根据原来DAO方法的注解类型来确定 调用什么
                // 思路: 解析原来DAO方法上的注解(注解的类型---调用方法，注解里面的SQL--调用方法的时候传参)

                // 1.获取方法上面的注解对象
                Annotation an = method.getAnnotations()[0];
                //2.获取注解对象对应的类型-----为了确定调用SglSession中的哪个方法
                Class annotationType = an.annotationType();

                //3。顺便解析注解对象里面value方法传递过来的sg1
                Method valueMethod = annotationType.getMethod("value");
                String[] sqlArry = (String[])valueMethod.invoke(an);

                String sql =sqlArry[0];
                System.out.println("这条sql是："+ sql);
                //调用方法的时候，obj参数可能是null值  三元运算符
                Object obj = args==null?null:args[0];
                //通过annotationType判断，该调用的是SglSession中的哪个方法
                if(annotationType == Insert.class){
                    Sqlsession.this.insert(sql,obj);
                }else if(annotationType == Update.class){
                    Sqlsession.this.update(sql,obj);
                }else if(annotationType == Delete.class){
                    Sqlsession.this.delete(sql,obj);
                }else if(annotationType == Select.class){
                    //根据返回值判断是查询多条还是单条
                    //1.分析原来DAO类中方法(查询)的返回值类型
                    Class methodReturnType = method.getReturnType();
                    if (methodReturnType == List.class){
                        //如果是查询多条记录，此时的方法返回值类型应该是一个List
                        // 需要将集合里面的泛型的类型获取出来，传递给下面的方法执行
                        // a.获取方法返回值的全部内容
                        Type returnType = method.getGenericReturnType();//java.util.List
                        //b.将这个type类型还原为可以操作泛型的那个类型
                        ParameterizedTypeImpl realReturnType = (ParameterizedTypeImpl)returnType;
                        //c.获取realReturnType中的泛型类
                        Type[] patternTypes = realReturnType.getActualTypeArguments();
                        //d.获取patternTypes数组的第一个元素即可
                        Type patternType = patternTypes[0];
                        //e.将patternType还原成原来的class即可 type 是class的父类
                        Class resultType = (Class)patternType;

                        return Sqlsession.this.selectList(sql,obj,resultType);

                    }else{//单条
                        return Sqlsession.this.selectOne(sql,obj,methodReturnType);
                    }

                }else{
                    System.out.println("这里出异常了！！");
                }
                return null;
            }
        };
        //创建一个代理对象
        Object obj = Proxy.newProxyInstance(classLoader, interfaces, h);
        return (T) obj;
    }

    /**
     * 解析sql中#{}中的内容
     * @param sql
     * @return
     */
    public SqlAndKey sqlHanding(String sql){
        StringBuilder newSql = new StringBuilder();
        List<String> keyList = new ArrayList<>();

        //解析sql
        while (true){
            //先搜索#{}的位置
            int left = sql.indexOf("#{");
            int right = sql.indexOf("}");
            //判断两个符号是否能组成一队
            if(left != -1 && right != -1 && left < right){
                //截取#{前面的部分，存入newsql中
                newSql.append(sql.substring(0,left));
                //在newSql中拼接一个？号
                newSql.append("?");
                //将#{key}这个key截取出来，存入集合
                keyList.add(sql.substring(left+2 , right));
            }else{
                //将剩余的sql补上
                newSql.append(sql);
//                System.out.println("sql = "+newSql);
//                System.out.println("key = "+ keyList);
                break;
            }

            sql = sql.substring(right+1);
        }
        SqlAndKey sqlAndKey = new SqlAndKey(newSql,keyList);
        return sqlAndKey;
    }

    //对象容器
    class SqlAndKey{
        StringBuilder newSql = new StringBuilder();
        List<String> keyList = new ArrayList<>();

        public SqlAndKey(StringBuilder newSql, List<String> keyList) {
            this.newSql = newSql;
            this.keyList = keyList;
        }

        public StringBuilder getNewSql() {
            return newSql;
        }

        public void setNewSql(StringBuilder newSql) {
            this.newSql = newSql;
        }

        public List<String> getKeyList() {
            return keyList;
        }

        public void setKeyList(List<String> keyList) {
            this.keyList = keyList;
        }

        @Override
        public String toString() {
            return "SqlAndKey{" +
                    "newSql=" + newSql +
                    ", keyList=" + keyList +
                    '}';
        }
    }
}
